package net.dotool;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;

import net.dotool.i.IFileCallback;

/**
 * @author 李岩飞
 * @email eliyanfei@126.com 2018年3月1日 上午9:59:54
 */
public class FileTools {
	/** Class文件扩展名 */
	public static final String CLASS_EXT = ".class";
	/** Jar文件扩展名 */
	public static final String JAR_FILE_EXT = ".jar";
	/** 在Jar中的路径jar的扩展名形式 */
	public static final String JAR_PATH_EXT = ".jar!";

	/**
	 * 获取文件名称[不含后缀名] 不去掉文件目录的空格
	 * 
	 * @param
	 * @return String
	 */
	public static String getFilePrefix(String fileName) {
		int splitIndex = fileName.lastIndexOf(".");
		return fileName.substring(0, splitIndex);
	}

	/**
	 * 获取文件扩展名
	 * 
	 * @param filename
	 * @return
	 */
	public static String getExtend(String filename) {
		return getExtend(filename, "");
	}

	/**
	 * 获取文件扩展名
	 * 
	 * @param filename
	 * @return
	 */
	public static String getExtend(String filename, String defExt) {
		if ((filename != null) && (filename.length() > 0)) {
			int i = filename.lastIndexOf('.');
			if ((i > 0) && (i < (filename.length() - 1))) {
				return (filename.substring(i + 1)).toLowerCase();
			}
		}
		return defExt.toLowerCase();
	}

	/**
	 * 修改文件名
	 * 
	 * @param filePath
	 *            需改名的文件
	 * @param definedName
	 *            自定义名称
	 * @return
	 */
	public static boolean rename(String filePath, String definedName) {
		File file = new File(filePath);
		if (file.exists()) {
			String fileExt = file.getName().substring(file.getName().lastIndexOf(".") + 1).toLowerCase();
			String rename = file.getParent() + "/" + definedName + "." + fileExt;
			return file.renameTo(new File(rename));
		} else {
			return false;
		}
	}

	/**
	 * 删除单个文件
	 * 
	 * @param fileName
	 *            被删除的文件名
	 * @return 如果删除成功，则返回true，否则返回false
	 */
	public static boolean deleteFile(String fileName) {
		return deleteFile(fileName, null);
	}

	public static boolean deleteFile(String fileName, IFileCallback callback) {
		File file = new File(fileName);
		if (file.exists() && file.isFile()) {
			if (callback == null) {
				return file.delete();
			} else if (callback.ok(file)) {
				return file.delete();
			}
		} else {
			return true;
		}
		return false;
	}

	public static boolean deleteDirectory(String dirName) {
		return deleteDirectory(dirName, null);
	}

	/**
	 * 删除目录及目录下的文件
	 * 
	 * @param dirName
	 *            被删除的目录所在的文件路径
	 * @return 如果目录删除成功，则返回true，否则返回false
	 */
	public static boolean deleteDirectory(String dirName, IFileCallback callback) {
		File dirFile = new File(dirName);
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return true;
		}
		boolean flag = true;
		// 列出全部文件及子目录
		File[] files = dirFile.listFiles();
		for (File file : files) {
			// 删除子文件
			if (file.isFile()) {
				flag = deleteFile(file.getAbsolutePath());
				// 如果删除文件失败，则退出循环
				if (!flag) {
					break;
				}
			} else if (file.isDirectory()) {// 删除子目录
				flag = deleteDirectory(file.getAbsolutePath(), callback);
				// 如果删除子目录失败，则退出循环
				if (!flag) {
					break;
				}
			}
		}

		if (!flag) {
			return false;
		}
		return dirFile.delete();
	}

	/**
	 * 删除文件，可以删除单个文件或文件夹
	 * 
	 * @param fileName
	 *            被删除的文件名
	 * @return 如果删除成功，则返回true，否是返回false
	 */
	public static boolean delFile(String fileName) {
		File file = new File(fileName);
		if (!file.exists()) {
			return true;
		} else {
			if (file.isFile()) {
				return deleteFile(fileName);
			} else {
				return deleteDirectory(fileName);
			}
		}
	}

	/**
	 * 复制单个文件，如果目标文件存在，则不覆盖
	 * 
	 * @param srcFileName
	 *            待复制的文件名
	 * @param descFileName
	 *            目标文件名
	 * @return 如果复制成功，则返回true，否则返回false
	 */
	public static boolean copyFile(String srcFileName, String descFileName) {
		return copyFileCover(srcFileName, descFileName, false);
	}

	/**
	 * 复制单个文件
	 * 
	 * @param srcFileName
	 *            待复制的文件名
	 * @param descFileName
	 *            目标文件名
	 * @param coverlay
	 *            如果目标文件已存在，是否覆盖
	 * @return 如果复制成功，则返回true，否则返回false
	 */
	public static boolean copyFileCover(String srcFileName, String descFileName, boolean coverlay) {
		File srcFile = new File(srcFileName);
		if (!srcFile.exists()) { // 判断源文件是否存在
			return false;
		} else if (!srcFile.isFile()) {// 判断源文件是否是合法的文件
			return false;
		}
		File descFile = new File(descFileName);
		// 判断目标文件是否存在
		if (descFile.exists()) {
			// 如果目标文件存在，并且允许覆盖
			if (coverlay) {
				if (!delFile(descFileName)) {
					return false;
				}
			} else {
				return false;
			}
		} else {
			if (!descFile.getParentFile().exists()) {
				// 如果目标文件所在的目录不存在，则创建目录
				// 创建目标文件所在的目录
				if (!descFile.getParentFile().mkdirs()) {
					return false;
				}
			}
		}

		// 准备复制文件
		// 读取的位数
		int readByte = 0;
		InputStream ins = null;
		OutputStream outs = null;
		try {
			// 打开源文件
			ins = new FileInputStream(srcFile);
			// 打开目标文件的输出流
			outs = new FileOutputStream(descFile);
			byte[] buf = new byte[1024];
			// 一次读取1024个字节，当readByte为-1时表示文件已经读取完毕
			while ((readByte = ins.read(buf)) != -1) {
				// 将读取的字节流写入到输出流
				outs.write(buf, 0, readByte);
			}
			return true;
		} catch (Exception e) {
			return false;
		} finally {
			// 关闭输入输出流，首先关闭输出流，然后再关闭输入流
			IoTools.closeIos(outs);
			IoTools.closeIos(ins);
		}
	}

	/**
	 * 复制整个目录的内容，如果目标目录存在，则不覆盖
	 * 
	 * @param srcDirName
	 *            源目录名
	 * @param descDirName
	 *            目标目录名
	 * @return 如果复制成功返回true，否则返回false
	 */
	public static boolean copyDirectory(String srcDirName, String descDirName) {
		return copyDirectoryCover(srcDirName, descDirName, false);
	}

	/**
	 * 复制整个目录的内容
	 * 
	 * @param srcDirName
	 *            源目录名
	 * @param descDirName
	 *            目标目录名
	 * @param coverlay
	 *            如果目标目录存在，是否覆盖
	 * @return 如果复制成功返回true，否则返回false
	 */
	public static boolean copyDirectoryCover(String srcDirName, String descDirName, boolean coverlay) {
		File srcDir = new File(srcDirName);
		// 判断源目录是否存在
		if (!srcDir.exists()) {
			return false;
		}
		// 判断源目录是否是目录
		else if (!srcDir.isDirectory()) {
			return false;
		}
		// 如果目标文件夹名不以文件分隔符结尾，自动添加文件分隔符
		String descDirNames = descDirName;
		if (!descDirNames.endsWith(File.separator)) {
			descDirNames = descDirNames + File.separator;
		}
		File descDir = new File(descDirNames);
		// 如果目标文件夹存在
		if (descDir.exists()) {
			if (coverlay) {
				// 允许覆盖目标目录
				if (!delFile(descDirNames)) {
					return false;
				}
			} else {
				return false;
			}
		} else {
			// 创建目标目录
			if (!descDir.mkdirs()) {
				return false;
			}

		}

		boolean flag = true;
		// 列出源目录下的所有文件名和子目录名
		File[] files = srcDir.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 如果是一个单个文件，则直接复制
			if (files[i].isFile()) {
				flag = copyFile(files[i].getAbsolutePath(), descDirName + files[i].getName());
				// 如果拷贝文件失败，则退出循环
				if (!flag) {
					break;
				}
			}
			// 如果是子目录，则继续复制目录
			if (files[i].isDirectory()) {
				flag = copyDirectory(files[i].getAbsolutePath(), descDirName + files[i].getName());
				// 如果拷贝目录失败，则退出循环
				if (!flag) {
					break;
				}
			}
		}
		return flag;
	}

	/**
	 * 将从输入流中读取数据并写入到输出流
	 * 
	 * @param in
	 * @param out
	 * @return 整个过程拷贝的字节数据数
	 * @throws IOException
	 */
	public static int copyStream(final InputStream in, final OutputStream out) throws IOException {
		int readedCount = 0;
		final byte[] tmpBuf = new byte[1024];// 10K
		while (true) {
			final int numRead = in.read(tmpBuf);
			if (numRead == -1)
				break;
			out.write(tmpBuf, 0, numRead);
			out.flush();
			readedCount += numRead;
		}
		return readedCount;
	}

	/**
	 * 创建File对象，自动识别相对或绝对路径，相对路径将自动从ClassPath下寻找
	 * 
	 * @param path
	 *            文件路径
	 * @return File
	 */
	public static File file(String path) {
		if (StringTools.isBlank(path)) {
			throw new NullPointerException("File path is blank!");
		}
		return new File(path);
	}

	/**
	 * 创建File对象
	 * 
	 * @param parent
	 *            父目录
	 * @param path
	 *            文件路径
	 * @return File
	 */
	public static File file(String parent, String path) {
		if (StringTools.isBlank(path)) {
			throw new NullPointerException("File path is blank!");
		}
		return new File(parent, path);
	}

	/**
	 * 创建File对象
	 * 
	 * @param parent
	 *            父文件对象
	 * @param path
	 *            文件路径
	 * @return File
	 */
	public static File file(File parent, String path) {
		if (StringTools.isBlank(path)) {
			throw new NullPointerException("File path is blank!");
		}
		return new File(parent, path);
	}

	/**
	 * 通过多层目录参数创建文件
	 * 
	 * @param directory
	 *            父目录
	 * @param names
	 *            元素名（多层目录名）
	 * @return the file 文件
	 * @since 4.0.6
	 */
	public static File file(File directory, String... names) {
		Assert.notNull(directory, "directorydirectory must not be null");
		if (ArrayTools.isEmpty(names)) {
			return directory;
		}

		File file = directory;
		for (String name : names) {
			if (null != name) {
				file = new File(file, name);
			}
		}
		return file;
	}

	/**
	 * 通过多层目录创建文件
	 * 
	 * 元素名（多层目录名）
	 * 
	 * @return the file 文件
	 * @since 4.0.6
	 */
	public static File file(String... names) {
		if (ArrayTools.isEmpty(names)) {
			return null;
		}

		File file = null;
		for (String name : names) {
			if (file == null) {
				file = new File(name);
			} else {
				file = new File(file, name);
			}
		}
		return file;
	}

	/**
	 * 创建File对象
	 * 
	 * @param uri
	 *            文件URI
	 * @return File
	 */
	public static File file(URI uri) {
		if (uri == null) {
			throw new NullPointerException("File uri is null!");
		}
		return new File(uri);
	}

	/**
	 * 创建File对象
	 * 
	 * @param url
	 *            文件URL
	 * @return File
	 */
	public static File file(URL url) {
		return new File(URLTools.toURI(url));
	}

	/**
	 * 给定路径已经是绝对路径<br>
	 * 此方法并没有针对路径做标准化，建议先执行{@link #normalize(String)}方法标准化路径后判断
	 * 
	 * @param path
	 *            需要检查的Path
	 * @return 是否已经是绝对路径
	 */
	public static boolean isAbsolutePath(String path) {
		if (StringTools.isEmpty(path)) {
			return false;
		}

		if (StringTools.C_SLASH == path.charAt(0) || path.matches("^[a-zA-Z]:[/\\\\].*")) {
			// 给定的路径已经是绝对路径了
			return true;
		}
		return false;
	}
}
